Skip to content

feat(ai): BYOK provider layer; drop GRIDA_LOCALDEV_SUPERUSER#718

Merged
softmarshmallow merged 6 commits into
mainfrom
feat/byok-ai-provider
May 18, 2026
Merged

feat(ai): BYOK provider layer; drop GRIDA_LOCALDEV_SUPERUSER#718
softmarshmallow merged 6 commits into
mainfrom
feat/byok-ai-provider

Conversation

@softmarshmallow

@softmarshmallow softmarshmallow commented May 17, 2026

Copy link
Copy Markdown
Member

Summary

  • BYOK layer. When BYOK_OPENROUTER_API_KEY (OpenRouter via @ai-sdk/openai-compatible) or BYOK_AI_GATEWAY_API_KEY (a dedicated Vercel AI Gateway key) is set, grida/model return a bare provider that structurally bypasses the billing seam — no gate, no Metronome ingest, no balance read. The contributor's own key pays the provider. Fail-closed: empty/unset ⇒ the billed path. Precedence: OpenRouter, then AI Gateway.
  • NEXT_PUBLIC_GRIDA_LOCALDEV_SUPERUSER removed entirely (it was AI-only). Its billing-bypass and auth-bypass holes are gone; checkGate/MissingOrgIdError are now unconditional on the billed path — a net GRIDA-SEC-003 hardening.
  • BYOK bypasses billing only — never auth. requireOrganizationId + route/action auth always run (verified live: unauth → 401).

Security — why this is safe

This cannot be enabled on, or leaked from, the hosted product:

  • Server-only, not NEXT_PUBLIC_. BYOK_OPENROUTER_API_KEY / BYOK_AI_GATEWAY_API_KEY are read via process.env in server-only seam code (editor/lib/ai/models.ts). Next.js only inlines NEXT_PUBLIC_* into the client bundle — these are never shipped to the browser. Zero secret-leakage surface.
  • Clone-and-local / self-host only. The hosted product (grida.co) does not set these vars and exposes no UI or request path to set them. An attacker using the hosted app cannot turn BYOK on, supply a key, or read one. Same server-env trust model as the existing OPENAI_API_KEY / REPLICATE_API_TOKEN secrets.
  • Strictly more secure than what it replaces. The removed NEXT_PUBLIC_GRIDA_LOCALDEV_SUPERUSER was a public flag that bypassed billing and auth. Replacing it with a server-only secret that bypasses billing only (auth always enforced) removes a public bypass and tightens the billed path (MissingOrgIdError/gate now unconditional).
  • Fail-closed. byok resolves to null unless a key env var is a non-empty string → any ambiguity falls back to the billed path. Resolved once at module load (no per-request attack surface).
  • Documented carve-out. SECURITY.md GRIDA-SEC-003 records the intentional billing-only bypass and the residual risk (a BYOK_* secret must never be set on a hosted/preview deploy — operational, by the same trust model as every other server secret).

Behavior change (breaking for local dev)

Local AI without a BYOK key now requires real billing (gate + Metronome) and always requires auth (login + resolvable org). The superuser no-auth/no-billing loop is gone by design — the supported local path is "set a BYOK key (still authenticated)". docs/contributing/billing.md documents the BYOK shortcut; SECURITY.md GRIDA-SEC-003 documents the carve-out + residual risk; test/billing-quota-and-ai.md TC-035/036 retargeted.

Test plan

  • turbo typecheck --filter=editor --force — 29/29
  • AI-seam unit tests 13/13 (new BYOK withAiAuth test: auth runs, refreshBalance skipped, balanceCents:0)
  • oxlint 0 errors · audit-ai-seam.ts 0 violations (1862 files) · repo grep: no superuser refs
  • Live dev server: BYOK key loaded from .env.local, seam module graph loads (no 500 ⇒ createOpenAICompatible construction sound); POST /private/ai/chat unauth → 401 (auth enforced)
  • Reviewer: authed end-to-end OpenRouter hit + no 402 gate (blocked locally by an unrelated Supabase service-role/seed mismatch, not the BYOK change — see assertOrgMember path)
  • Security review of the GRIDA-SEC-003 carve-out (required for tagged-boundary changes)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Contributor BYOK: set a BYOK key to run AI text/chat without billing; auth still required.
    • Billing UI: new credit chip shows BYOK badge, balances, and refresh control.
  • Security

    • Removed local-dev superuser bypass; authentication and organization enforcement are now unconditional.
  • Documentation

    • Updated security and billing docs with BYOK workflow, limits (text/chat only), precedence, and deployment cautions.

Review Change Stack

Contributors had no way to exercise AI features locally without a
Vercel AI Gateway key + Metronome billing wired up. The only escape
was NEXT_PUBLIC_GRIDA_LOCALDEV_SUPERUSER, a public flag that bypassed
both billing AND auth.

Introduce a BYOK layer: when BYOK_OPENROUTER_API_KEY (OpenRouter via
@ai-sdk/openai-compatible) or BYOK_AI_GATEWAY_API_KEY (dedicated AI
Gateway key) is set, `grida`/`model` return a bare provider that
structurally bypasses the billing seam (no gate, no Metronome ingest,
no balance). The contributor's own key pays the provider directly.

BYOK bypasses billing ONLY, never auth: requireOrganizationId and
route/action auth always run. The old superuser flag is removed
entirely, which makes the MissingOrgIdError/gate contract
unconditional on the billed path (a net GRIDA-SEC-003 hardening).

Behavior change: local AI without a BYOK key now requires real
billing + always requires auth; the supported local path is "set a
BYOK key". SECURITY.md documents the carve-out + residual risk;
test/billing-quota-and-ai.md TC-035/036 retargeted.
@vercel

vercel Bot commented May 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
grida Ready Ready Preview, Comment May 18, 2026 10:55am
6 Skipped Deployments
Project Deployment Actions Updated (UTC)
code Ignored Ignored May 18, 2026 10:55am
docs Ignored Ignored Preview May 18, 2026 10:55am
legacy Ignored Ignored May 18, 2026 10:55am
backgrounds Skipped Skipped May 18, 2026 10:55am
blog Skipped Skipped May 18, 2026 10:55am
viewer Skipped Skipped May 18, 2026 10:55am

Request Review

@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1c03f01a-138e-4f5b-8a38-8e1cc24a5842

📥 Commits

Reviewing files that changed from the base of the PR and between 772b15a and 786e8a9.

📒 Files selected for processing (2)
  • editor/lib/ai/actions/chat.ts
  • editor/lib/ai/server.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • editor/lib/ai/actions/chat.ts
  • editor/lib/ai/server.ts

Walkthrough

This PR replaces the local-dev superuser bypass with a BYOK (Bring Your Own Keys) billing mode that enforces authentication and organization resolution while allowing contributors to bypass Metronome billing/metering. It updates the AI seam provider routing, credit state shapes, UI components, environment configuration, security boundaries, and comprehensive test coverage.

Changes

BYOK Billing Bypass Implementation

Layer / File(s) Summary
BYOK provider resolution and selection
editor/lib/ai/models.ts
Adds resolveByokProvider() that checks BYOK_OPENROUTER_API_KEY (creates OpenAI-compatible with headers) or BYOK_AI_GATEWAY_API_KEY (creates Vercel AI Gateway), defines shared GATEWAY_ATTRIBUTION_HEADERS, and exports byok provider and isByokActive() helper.
Billing seam rework and BYOK routing
editor/lib/ai/server.ts
Removes local-dev superuser bypass from gate/transaction/streaming; refactors grida provider to route languageModel through dynamically selected bare BYOK vs billing-wrapped gateway; updates checkGate, withTransaction, streaming ingest, and re-exports isByokActive; adds byokBypass?: boolean option to withAiAuth.
withAiAuth BYOK runtime short-circuit
editor/lib/ai/server.ts
Adds runtime BYOK-only balance short-circuit: when isByokActive() and opts.byokBypass are true, returns balanceCents: 0 and skips refreshBalance; updates overload types to accept byokBypass.
Chat action with BYOK bypass
editor/lib/ai/actions/chat.ts
Refactors runChat to call withAiAuth with { byokBypass: true }, validates model id against an allowlist, constructs messages, calls generateText with Grida provider options, and returns reply with usage and computed costs.
Credit state shape and controller updates
editor/lib/ai/credits/actions.ts, editor/lib/ai/credits/controller.ts, editor/lib/ai/credits/provider.tsx
Adds byok: boolean to preload, controller state, and provider hook; initializes EMPTY.byok from isByokActive(); preserves byok on refresh()/consume() and documents BYOK scope in JSDoc.
Auth enforcement and organization validation
editor/env.ts, editor/app/(api)/private/ai/chat/route.ts, editor/app/(canvas)/canvas/tools/ai/generate.ts
Removes NEXT_PUBLIC_GRIDA_LOCALDEV_SUPERUSER / Env.IS_LOCALDEV_SUPERUSER; chat route now requires Supabase auth and requireOrganizationId unconditionally and always passes resolved organizationId; extractContext now throws MissingOrgIdError when org id absent.
Credit display UI components
editor/app/(www)/(ai)/ai/_page.tsx
Introduces CreditChip reusable component showing BYOK badge, no-balance badge, or billing link with below-floor messaging and refresh button; replaces inline header UI and gates the below-floor callout on !credits.byok.
Environment, linting, and dependency changes
editor/.env.example, editor/.oxlintrc.jsonc, editor/package.json, editor/scripts/audit-ai-seam.ts
Removes NEXT_PUBLIC_GRIDA_LOCALDEV_SUPERUSER from env example; adds BYOK/Vercel AI Gateway env docs; restricts direct imports of @ai-sdk/openai-compatible; adds the package to the audit forbid list and editor/package.json.
Security and contributor documentation
SECURITY.md, docs/contributing/billing.md, test/billing-quota-and-ai.md
Updates GRIDA-SEC-003 to document unconditional org-id enforcement with a BYOK carve-out; adds contributor BYOK workflow, troubleshooting, and env notes in billing docs; updates test-case doc scenarios from superuser bypass to BYOK billing bypass.
Test updates and coverage
editor/lib/ai/__tests__/server.test.ts, editor/lib/ai/credits/__tests__/controller.test.ts, packages/grida-fonts/__tests__/typr.test.ts, packages/grida-fonts/vitest.config.ts
Partially mocks isByokActive for tests, adds BYOK-specific withAiAuth tests, updates credits controller tests to include byok and BYOK persistence tests, expands font fixtures, and increases Vitest timeout for CI.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • gridaco/grida#711: Both PRs overlap on the AI seam org-id trust-boundary enforcement in editor/lib/ai/server.ts, with this PR further replacing the localdev bypass and adding BYOK carve-out semantics.
  • gridaco/grida#703: Related because that PR relied on Env.web.IS_LOCALDEV_SUPERUSER bypass which this PR removes, potentially conflicting with its audio-generation bypass behavior.

Suggested labels

documentation, packages

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat(ai): BYOK provider layer; drop GRIDA_LOCALDEV_SUPERUSER' accurately reflects the main objectives: introducing a BYOK provider layer for AI and removing the legacy superuser bypass mechanism.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/byok-ai-provider

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Under BYOK the seam forces balanceCents:0 and isByokActive() is
server-only, so the credit chip/banner showed a misleading
"$0 / out of credit / top up" while calls actually succeed via the
contributor's own key.

Plumb a `byok` flag from preloadAiCredits through the controller;
useAiCredits() now returns a discriminated union where the `byok`
variant omits cents/allowed/formatted, so every consumer is
compile-forced to handle BYOK and render its own label. The
low-balance / below-floor / top-up affordances are suppressed under
BYOK across the AI chat, music, and image surfaces.

Also correct a stale "non-superuser mode" comment in canvas
generate.ts (GRIDA-SEC-003 hygiene from the superuser removal).

GRIDA-SEC-003: isByokActive is re-exported through the seam
(@/lib/ai/server); only the boolean reaches the client, never the key.
The "parses STAT table structure correctly" test inherited the 5000ms
vitest default while synchronously parsing 4 large variable fonts,
making it flake on slower CI runners (5084ms observed). Match its
sibling multi-font tests with an explicit 20000ms timeout and drop a
duplicate Recursive parse that added runtime but no coverage.
@vercel vercel Bot temporarily deployed to Preview – viewer May 18, 2026 09:55 Inactive
@vercel vercel Bot temporarily deployed to Preview – backgrounds May 18, 2026 09:55 Inactive
@vercel vercel Bot temporarily deployed to Preview – blog May 18, 2026 09:55 Inactive
@softmarshmallow softmarshmallow marked this pull request as ready for review May 18, 2026 09:56

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/contributing/billing.md (1)

1-3: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add Markdown frontmatter with format: md.

This file is plain Markdown and should opt out of MDX parsing.

Proposed fix
+---
+format: md
+---
+
 # Contributing to Grida | Billing
As per coding guidelines, "`docs/**/*.md`: For files that don't use JSX/MDX features, add `format: md` to frontmatter to opt out of MDX parsing entirely and prevent angle-bracket issues".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/contributing/billing.md` around lines 1 - 3, Add a YAML frontmatter
block at the top of the "Contributing to Grida | Billing" Markdown file (the
header line shown in the diff) that sets format: md so the file opts out of MDX
parsing; insert the frontmatter before the existing "# Contributing to Grida |
Billing" header and ensure it only contains format: md.
🧹 Nitpick comments (1)
test/billing-quota-and-ai.md (1)

202-210: 🏗️ Heavy lift

Move BYOK billing-seam invariants to automated tests instead of manual UX cases.

Lines 202-210 describe server-side billing/auth logic, not human-interaction UX behavior; this should live in automated tests, with this manual file focused on interaction-only checks.

As per coding guidelines, "test/**/*.md: Add manual test cases to the test directory only for UX bugs requiring human interaction verification ... not for pure logic, math, or data transformations".

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/contributing/billing.md`:
- Line 26: Replace the markdown link to ../../SECURITY.md in the sentence
starting with "Never set `BYOK_*` on a hosted or preview deploy." with inline
code (e.g., use ``../../SECURITY.md`` instead of a markdown link) so the docs
page no longer links outside /docs; update the sentence containing `BYOK_*`
accordingly and verify there are no other references linking outside /docs in
docs/contributing/billing.md.

---

Outside diff comments:
In `@docs/contributing/billing.md`:
- Around line 1-3: Add a YAML frontmatter block at the top of the "Contributing
to Grida | Billing" Markdown file (the header line shown in the diff) that sets
format: md so the file opts out of MDX parsing; insert the frontmatter before
the existing "# Contributing to Grida | Billing" header and ensure it only
contains format: md.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2253d41f-9134-4bc2-9990-194f5bffd042

📥 Commits

Reviewing files that changed from the base of the PR and between 5b6e8ce and 0b4bc8e.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (22)
  • SECURITY.md
  • docs/contributing/billing.md
  • editor/.env.example
  • editor/.oxlintrc.jsonc
  • editor/app/(api)/private/ai/chat/route.ts
  • editor/app/(canvas)/canvas/tools/ai/generate.ts
  • editor/app/(tools)/(playground)/playground/image/_page.tsx
  • editor/app/(www)/(ai)/ai/_page.tsx
  • editor/app/(www)/(ai)/ai/music/playground/_page.tsx
  • editor/env.ts
  • editor/lib/ai/__tests__/server.test.ts
  • editor/lib/ai/credits/__tests__/controller.test.ts
  • editor/lib/ai/credits/actions.ts
  • editor/lib/ai/credits/controller.ts
  • editor/lib/ai/credits/index.ts
  • editor/lib/ai/credits/provider.tsx
  • editor/lib/ai/models.ts
  • editor/lib/ai/server.ts
  • editor/package.json
  • editor/scripts/audit-ai-seam.ts
  • packages/grida-fonts/__tests__/typr.test.ts
  • test/billing-quota-and-ai.md
💤 Files with no reviewable changes (1)
  • editor/env.ts

Comment thread docs/contributing/billing.md Outdated
- Bypasses **billing only — never auth.** Still sign in (`insider@grida.co` / `password`); a resolvable org is still required (an unauthenticated request still 401s).
- **Text/chat only** — OpenRouter exposes no image/audio models. Catalog model IDs are unchanged; use IDs your provider accepts (edit `editor/lib/ai/models.ts` locally if one 404s).
- Precedence if both are set: OpenRouter, then AI Gateway. Fail-closed — an empty/unset key falls back to the billed path.
- **Never set `BYOK_*` on a hosted or preview deploy.** It disables billing **and** the org-id sanity gate for every org. Contributor / self-host / local only. See [SECURITY.md](../../SECURITY.md) `GRIDA-SEC-003` (BYOK carve-out).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Do not link to repository paths outside /docs from docs pages.

Line 26 links to ../../SECURITY.md; use inline code for that repo path instead of a markdown link.

Proposed fix
-- **Never set `BYOK_*` on a hosted or preview deploy.** It disables billing **and** the org-id sanity gate for every org. Contributor / self-host / local only. See [SECURITY.md](../../SECURITY.md) `GRIDA-SEC-003` (BYOK carve-out).
+- **Never set `BYOK_*` on a hosted or preview deploy.** It disables billing **and** the org-id sanity gate for every org. Contributor / self-host / local only. See `SECURITY.md` (`GRIDA-SEC-003`, BYOK carve-out).
As per coding guidelines, "Never link outside `/docs` from docs markdown files; reference external paths as inline code instead (e.g., `` `crates/grida/examples/foo.rs` ``)".
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- **Never set `BYOK_*` on a hosted or preview deploy.** It disables billing **and** the org-id sanity gate for every org. Contributor / self-host / local only. See [SECURITY.md](../../SECURITY.md) `GRIDA-SEC-003` (BYOK carve-out).
- **Never set `BYOK_*` on a hosted or preview deploy.** It disables billing **and** the org-id sanity gate for every org. Contributor / self-host / local only. See `SECURITY.md` (`GRIDA-SEC-003`, BYOK carve-out).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/contributing/billing.md` at line 26, Replace the markdown link to
../../SECURITY.md in the sentence starting with "Never set `BYOK_*` on a hosted
or preview deploy." with inline code (e.g., use ``../../SECURITY.md`` instead of
a markdown link) so the docs page no longer links outside /docs; update the
sentence containing `BYOK_*` accordingly and verify there are no other
references linking outside /docs in docs/contributing/billing.md.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0b4bc8efc8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread editor/lib/ai/server.ts Outdated
Comment on lines +792 to +797
if (isByokActive()) {
// GRIDA-SEC-003 BYOK carve-out: no Grida balance to read.
return {
success: true,
data: { ...data, balanceCents: 0 } as AiActionData<T>,
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Limit BYOK balance short-circuit to truly unbilled calls

withAiAuth now returns balanceCents: 0 whenever any BYOK_* key is present, but several actions wrapped by withAiAuth still execute through billed paths (for example generateAudio calls methods.generateAudio, which uses runPrediction/withTransaction and still gates+ingests usage). In BYOK mode this makes clients treat credits as non-billed while those requests can still spend or block on real balance, so users can silently drain credit and then hit unexpected 402s. The BYOK shortcut should only skip balance reads for call paths that actually bypass the billing middleware.

Useful? React with 👍 / 👎.

Comment thread editor/lib/ai/models.ts Outdated
Comment on lines +280 to +281
const openrouterKey = process.env.BYOK_OPENROUTER_API_KEY;
if (openrouterKey) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate BYOK keys before activating override provider

The BYOK switch currently treats any truthy env string as active (if (openrouterKey) / if (aiGatewayKey)), so whitespace-only or malformed secrets still force the bare-provider path instead of falling back to billed mode. In that state all AI SDK calls can fail against an invalid upstream key while billing is bypassed, which is a hard outage caused by minor env formatting mistakes (e.g., a space/newline in secret value). Normalizing/validating keys (e.g., trim + non-empty) before enabling BYOK would preserve the intended fail-closed behavior.

Useful? React with 👍 / 👎.

Per-test timeout bumps were whack-a-mole: after stabilizing the STAT
test, "parses Geist variable font" then flaked at 5092ms. Any single
large variable-font parse can exceed the 5000ms vitest default on a
loaded CI runner, so raise it package-wide to 30000ms and drop the
now-redundant per-test override.
Addresses the PR #718 AI review (Codex P1/P2, CodeRabbit).

P1 (silent drain): `withAiAuth`'s `balanceCents:0` short-circuit fired
for every action whenever a BYOK key was set, but BYOK only swaps the
AI-SDK provider — Replicate-backed actions (audio/image) still run
`withTransaction` and bill. Those calls spent real credit and could
402 while the client was told "0 / unlimited". The short-circuit is
now opt-gated (`byokBypass`, default `false`); only `ai/chat` (AI-SDK
text) sets it. Billed actions read the real balance under BYOK.

UI: dropped the global discriminated union — it wrongly forced a
"BYOK" label onto Replicate-billed surfaces. `useAiCredits()` returns
a flat `byok` flag; only the AI chat page acts on it. Music & image
playgrounds reverted to the normal balance view.

P2: BYOK keys are trimmed; whitespace-only falls back to the billed
path (strengthens the documented fail-closed contract).

Docs: SECURITY.md GRIDA-SEC-003 + billing.md reworded — BYOK bypass
is the AI-SDK text path only; Replicate audio/image still gate+bill.
billing.md outside-/docs links → GitHub URLs; added `format: md`.

GRIDA-SEC-003: net hardening — narrows the bypass, never widens it;
auth + the unconditional billed-path gate are untouched.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@editor/lib/ai/actions/chat.ts`:
- Around line 103-108: The code currently accepts any catalog id (isCatalogId)
so callers can request hidden/reserved models; change the server-side check that
sets useModelId so it only allows models present in the public/tiered allowlist
(i.e., the same set used to render the picker) rather than any catalog entry.
Concretely, replace the isCatalogId(requested) predicate with a check against
the tiered/public model set (for example verify requested matches one of
catalog[tiers.*].id entries or add a helper like isTieredModel(requested)), then
keep the existing logic that picks grida(useModelId) vs model("mini") and
resolves resolvedId from the tier catalog to avoid desync. Ensure this
validation runs in the same function where requested/useModelId are computed so
hidden models cannot be selected by forging the payload.

In `@editor/lib/ai/server.ts`:
- Around line 443-453: The current switch assigns activeProvider = byok ??
wrappedProvider which causes gridaFn.imageModel to call the BYOK provider and
bypass billing; instead keep languageModel bound to the active provider but
force imageModel to always use the billing-wrapped gateway. Update gridaFn and
its properties so gridaFn(modelId) and gridaFn.languageModel(modelId) use
activeProvider.languageModel(modelId) while gridaFn.imageModel(modelId) always
delegates to wrappedProvider.imageModel(modelId) (so methods.getSDKImageModel()
remains metered).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dcc5cc63-ec1e-4f75-888b-1f4b58d8106f

📥 Commits

Reviewing files that changed from the base of the PR and between 3787b58 and 772b15a.

📒 Files selected for processing (8)
  • SECURITY.md
  • docs/contributing/billing.md
  • editor/app/(www)/(ai)/ai/_page.tsx
  • editor/lib/ai/__tests__/server.test.ts
  • editor/lib/ai/actions/chat.ts
  • editor/lib/ai/credits/provider.tsx
  • editor/lib/ai/models.ts
  • editor/lib/ai/server.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • editor/lib/ai/models.ts
  • editor/lib/ai/tests/server.test.ts

Comment thread editor/lib/ai/actions/chat.ts
Comment thread editor/lib/ai/server.ts Outdated
CodeRabbit follow-up on 772b15a:
- grida.imageModel() stays on the billing-wrapped provider; BYOK swaps
  only the language provider, so SDK image generation is still gated +
  metered under BYOK (matches SECURITY.md / billing.md GRIDA-SEC-003).
- runChat only accepts the 4 tier model ids (was: any catalog id), so
  a forged payload can't request reserved/non-tiered models.
@vercel vercel Bot temporarily deployed to Preview – blog May 18, 2026 10:51 Inactive
@vercel vercel Bot temporarily deployed to Preview – backgrounds May 18, 2026 10:51 Inactive
@vercel vercel Bot temporarily deployed to Preview – viewer May 18, 2026 10:51 Inactive
@softmarshmallow softmarshmallow merged commit b8c60ad into main May 18, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai AI models, prompts, and pricing enhancement New feature or request env

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant